<script setup lang="ts">
import { customChatEmits, customChatProps, Msg } from './type'

import { chatAi } from '@/serves/chatAi'
import { removeSpaces } from '@/utils/tool'
import { computed, nextTick, ref } from 'vue'
import { ElMessage } from 'element-plus'
import { MsgType, RenderType, Sender } from './enum'
import Markdown from './component/markdown.vue'
import { line, pie, bar } from './mockData/chat-js'
import Chatjs from './component/chatjs.vue'
import { cloneDeep } from 'lodash-es'
import { copyText } from 'web-plugin-tool'
import replyImg from '@/imgs/robot-logo.png'
import userImg from '@/imgs/user2.png'
import FileList from './component/fileList.vue'

const props = defineProps(customChatProps)
const emits = defineEmits(customChatEmits)

let itemContent = ref()
let msgType = ''

const chatComRef = ref<null | Element>(null)
// 消息列表
const msgList = ref<Msg[]>([])
// 中断请求
let abort: null | AbortController = null
// 所有的消息列表
const allMsgList = computed(() => {
	return [...props.defaultList, ...msgList.value]
})
// 头像
const avatar = computed(() => {
	let user = props.avatar?.user || userImg
	let reply = props.avatar?.reply || replyImg
	return {
		user,
		reply,
	}
})

function switchLoadStatus(status: boolean) {
	emits('update:msgLoadStatus', status)
}

// 发送消息
function sendMsg(data: { currentContent: any }) {
	console.log(data, '发送消息给后端')
	const text = data.currentContent
	delete data.currentContent

	if (!props.url) {
		console.error('请传入url')
		return
	}
	if (!text) {
		console.error('请传入currentContent, 当前发送的内容')
		return
	}
	msgList.value.push(
		{
			msgType: MsgType.Text,
			sender: Sender.User,
			content: text,
			thinkStatus: false,
			answerStatus: false,
			...data,
		},
		{
			msgType: MsgType.Text,
			sender: Sender.Ai,
			thinkStatus: true,
			answerStatus: true,
			serveData: {},
			content: '',
		}
	)
	switchLoadStatus(true)
	itemContent.value = ''
	msgType = ''

	// mock
	// setTimeout(() => {
	// 	onMsg(chatjsList)
	// 	switchLoadStatus(false)
	// }, 2000)
	// return
	// ---end--

	chatAi({
		url: props.url,
		method: props.method,
		data,
		isToJson: false,
		isStream: props.isStream,
		headers: {
			'Content-Type': 'application/json',
			...props.headers,
		},
		onOpen(abortController) {
			nextTick(() => {
				scrollToBottom()
			})
			console.log(abortController, '开始连接')
			abort = abortController
		},
		onMsg: onMsg,
		onClose(closeMsg) {
			abort = null
			switchLoadStatus(false)
			msgList.value.forEach((m: any) => {
				m.thinkStatus = false
				m.answerStatus = false
			})
			if (closeMsg === 'ok') {
				// 数据接收完成
				emits('onMsgEnd')
			}
			console.log(closeMsg, '连接关闭')
		},
		onError(error) {
			// console.log(error, '连接错误')
			ElMessage.error(error)

			// 如果出错，将最后一条消息的 thinkStatus 和 answerStatus 都设置为 false， 并提示错误信息
			const lastData = msgList.value[msgList.value.length - 1]
			msgList.value[msgList.value.length - 1] = {
				...lastData,
				thinkStatus: false,
				answerStatus: false,
				msgType: MsgType.Text,
				content: '服务器繁忙,请稍后再试!',
			}

			// 测试代码
			// onMsg(pie)
			// 测试代码结束

			nextTick(() => {
				switchLoadStatus(false)
				scrollToBottom()
				emits('onMsgEnd')
			})
		},
	})
}

// 接收消息
async function onMsg(msg: any) {
	console.log(msg, 'msg')
	if (msg && msg.includes && msg.includes('data: [DONE]')) {
		return
	}
	const arr = formatData(msg)
	arr.forEach((m: any) => {
		msgType = m.msgType
		if (props.isStream) {
			itemContent.value += m.content
		} else {
			itemContent.value = m.content
		}
	})
	// console.log(itemContent.value, 'itemContent.value')

	const lastData = msgList.value[msgList.value.length - 1]
	msgList.value[msgList.value.length - 1] = {
		...lastData,
		thinkStatus: false,
		msgType,
		content: itemContent.value,
		serveData: arr[arr.length - 1]?.serveData || {},
	}
	await nextTick()
	scrollToBottom()
}

/*
  转换数据格式
  1.解析数据
  2.判断用什么方式渲染
*/
function formatData(data: any) {
	/*
	{
		// 返回必须包含的两个字段
		msgType: MsgType.Md, // 什么类型
		content: 内容, // 内容
	}
	*/
	console.log(data, 'formatData')

	let list: any = []
	// 情况一 以data:{ 开头 并以 } 结尾的流式数据
	const newStr = removeSpaces(data)
	if (props.url.includes('/v3/chat/completions') && newStr.startsWith('data:{') && newStr.endsWith('}')) {
		/*
      data: {"choices":[{"delta":{"content":"。","role":"assistant"},"index":0}],"created":1742800953,"id":"0217428009525694aa63ed2acc75b1976f5c563db6a28ca3c6b23","model":"doubao-lite-128k-240828","service_tier":"default","object":"chat.completion.chunk","usage":null}
    */
		// 按行分割字符串
		const lines = data.split('\n')
		// 过滤掉空行，并去除 'data: ' 前缀
		const jsonStrings = lines
			.filter((line: string) => line.trim() !== '')
			.map((line: string) => line.replace('data: ', ''))
		// 解析每个 JSON 字符串
		const jsonObjects = jsonStrings.map((jsonString: string) => JSON.parse(jsonString))
		for (let index = 0; index < jsonObjects.length; index++) {
			const o = jsonObjects[index]
			o.choices.forEach((m: { delta: { content: string } }) => {
				list.push({
					...m.delta,
					msgType: MsgType.Md,
					content: m.delta.content,
				})
			})
		}
	}
	// 正常流数据 以 ‘ { ’ 开头， 并以 ’}‘结尾的
	else if (props.renderType === RenderType.Stream) {
		// console.log('正常流数据', JSON.parse(newStr));
		// 替换<think></think>为div 并保留所有属性和内容
		try {
			const obj = JSON.parse(data)
			const content = obj.message.content.replace(/<think>/g, '<div class="think">')
			const content2 = content.replace(/<\/think>/g, '</div>')
			list.push({
				...obj,
				msgType: MsgType.Md,
				content: content2,
			})
		} catch (error) {
			console.log('转换为JSON失败：', error)
		}
	}
	// 畸形接口2 智能对话
	else if (props.renderType === RenderType.MainChatV1) {
		const obj = data[0]
		list.push({
			msgType: MsgType.Md,
			serveData: obj,
			content: obj.answerContent,
		})
	}
	// 畸形接口1 （艾比森）
	else if (props.renderType === RenderType.AibisenChatV1) {
		const obj = data[0]
		list.push({
			msgType: MsgType.Md,
			content: obj,
		})
	}
	// 畸形接口
	else if (props.renderType === RenderType.Zhinengwenshu) {
		console.log('进来了吗')
		const obj = data[0]
		let type = MsgType.Md
		console.log(obj, 'obj123');

		if(obj.type === '折线图'){
			obj.type = 'line'
		}
		if(obj.type === '柱状图'){
			obj.type = 'bar'
		}
		if(obj.type === '饼状图'){
			obj.type = 'pie'
		}
		if(['line', 'bar', 'pie'].includes(obj.type)){
			type = MsgType.ChatJs
		}

		list.push({
			msgType: type,
			serveData: obj,
			content: type === MsgType.Md ? JSON.stringify(obj) : obj,
		})
	}
	// 这个接口可能重复
	else if (props.url.includes('/api/integration/flow/apiDef/call')) {
		// chatjs 图表
		const obj = data[0]
		list.push({
			msgType: MsgType.ChatJs,
			content: obj,
		})
	}
	return list
}

// 滚动底部的函数
function scrollToBottom() {
	if (chatComRef.value) {
		chatComRef.value.scrollTop = chatComRef.value.scrollHeight
	}
}

// 重置数据
function resetData() {
	itemContent.value = ''
	msgType = ''
	msgList.value.length = 0
	abort = null
	scrollToBottom()
}

// 重新回答
function handleRefresh(obj: any) {
	console.log(obj, 'obj')
	emits('onReAnswer', obj)
}

// 复制
function handleCopy(text: string) {
	copyText(text).then(() => {
		ElMessage.success('复制成功')
	})
}

// 暴露给父组件的方法
defineExpose({
	// 发送消息
	onSendMsg(data: { currentContent: any }) {
		sendMsg(data)
	},
	// 获取消息列表
	getMsgList() {
		// 合并默认数据和消息列表
		return cloneDeep(props.defaultList.concat(msgList.value))
	},
	// 关闭连接
	onClose() {
		abort?.abort()
	},
	// 滚动到底部
	onScrollToBottom() {
		scrollToBottom()
	},
	// 重置数据
	onReset: resetData,
})
</script>
<template>
	<div class="chat-com" ref="chatComRef">
		<div
			:class="['item', m.sender === Sender.User ? 'user-send' : '', m.answerStatus ? '' : 'answer-end']"
			v-for="(m, i) in allMsgList"
		>
			<div class="head">
				<img class="avatar user-avatar" v-if="m.sender === Sender.User" :src="avatar.user" alt="" />
				<img class="avatar" v-else :src="avatar.reply" alt="" />
			</div>
			<div class="msg-content">
				<!-- 加载 -->
				<div class="load-content" v-if="i === allMsgList.length - 1 && m.sender !== Sender.User">
					<!-- <span v-loading="true"></span> -->
					<!-- <span v-loading="m.answerStatus"></span> -->
					<span v-loading="m.thinkStatus"></span>
					<p :class="m.thinkStatus ? 'show' : ''">{{ props.thinkText }}</p>

					<!-- <span v-loading="true"></span>
					<p :class="m.thinkStatus ? 'show' : ''">思考中。。。。。。</p> -->
				</div>
				<!--
					 v-else-if="m.msgType === MsgType.FileList"
				 -->
				<FileList :fileList="m.content" v-if="m.sender === Sender.User && m.msgType === MsgType.FileList" />
				<Markdown v-if="m.msgType === MsgType.Md" :msg="m.content" />
				<Chatjs v-else-if="m.msgType === MsgType.ChatJs" :msg="m.content" />

				<div class="text" v-else>
					{{ m.content }}
				</div>
				<div class="bottom-ctrl" v-if="showReplyMsgCtrl && m.sender === Sender.Ai && !m.answerStatus">
					<el-tooltip class="box-item" effect="dark" content="重新回答" placement="top">
						<span v-debounce="() => handleRefresh(allMsgList[i - 1])">
							<el-icon><Refresh /></el-icon>
						</span>
					</el-tooltip>
					<el-tooltip class="box-item" effect="dark" content="复制" placement="top">
						<span @click="handleCopy(m.content)">
							<el-icon><DocumentCopy /></el-icon>
						</span>
					</el-tooltip>
				</div>
				<slot></slot>
			</div>
		</div>
	</div>
</template>
<style lang="scss" scoped>
.chat-com {
	$barWidth: 4px;
	width: calc(100% + $barWidth);
	height: 100%;
	overflow-y: auto;
	padding: 0 20px 20px 0;
	&::-webkit-scrollbar {
		width: $barWidth !important;
		height: $barWidth !important;
	}
	.item {
		display: flex;
		margin-top: 20px;
		.head {
			img {
				width: 44px;
			}
		}
		.msg-content {
			max-width: calc(100% - 88px);
			margin-left: 10px;
			background: #fff;
			padding: 12px 15px;
			border-radius: 8px;
			position: relative;
			padding-left: 26px;
			min-width: 150px;
			.load-content {
				position: absolute;
				left: -4px;
				top: 12px;
				display: flex;
				width: 150px;
				span {
					width: 20px;
					transform: scale(0.5);
					margin-right: 10px;
					:deep(.el-loading-mask) {
						background: transparent !important;
					}
				}
				p {
					opacity: 0;
					&.show {
						opacity: 1;
					}
				}
			}
		}
		// flex-direction: column;
		// 用户
		&.user-send {
			// display: flex;
			flex-direction: row-reverse;
			align-items: center;
			.load-content {
				display: none;
			}
			.msg-content {
				background: #eaeef7;
				margin-right: 10px;
				padding-left: 0;
				min-width: 0;
				.file-list {
					display: flex;
				}
				.text {
					font-size: 16px;
					color: #000;
					background: #eaeef7;
					margin-left: 15px;
					.loading {
						display: none;
					}
				}
			}
		}
		// 回答结束
		&.answer-end {
			.msg-content {
				transition: margin 0.15s ease-in-out;
				// padding-left: 0;
				// margin-left: 0;
			}
		}
		.bottom-ctrl {
			margin-top: 16px;
			.el-icon {
				margin-right: 20px;
				color: #999;
				cursor: pointer;
			}
		}
	}
}
</style>
